feat: ship an installable crosscode npm package - #30
Merged
Conversation
Nothing in this repo compiled: the root `build` script was `tsc --noEmit`, every package was private, and both declared `bin` entries pointed at TypeScript sources that plain Node cannot execute. Bundle three entrypoints with esbuild instead -- `crosscode`, `crosscode-mcp`, and the daemon the MCP server spawns -- and publish one package from the root manifest. Bundling also sidesteps the cross-package relative imports (`../../daemon/src/client.js`) that escape any per-package tarball root, so no import rewriting is needed to ship. scripts/build.mjs fails the build if anything from node_modules is inlined: a bundled-but-undeclared dependency works in the monorepo and breaks only once the tarball is installed somewhere else. Also fixes two things that only fail after install: - The MCP bootstrap hardcoded the monorepo layout (`node_modules/.bin/tsx` plus `apps/daemon/src/main.ts`) and spawned it with `stdio: "ignore"`, so in an installed package it failed silently and the client saw DAEMON_UNAVAILABLE forever. resolveDaemonLaunch now prefers the bundled daemon run by process.execPath, keeps an existence-checked monorepo fallback for `pnpm mcp`, and surfaces a message naming what was tried. - The CLI's main-module guard compared import.meta.url against argv[1] as given. npm installs a bin as a symlink, so the installed `crosscode` binary parsed nothing and exited silently. Compare against the realpath. Verified from a tarball installed outside this repo on Node 24 alone: --help, init, status, and commands all work; an MCP initialize + tools/list handshake returns 22 tools; and the bootstrap starts a real daemon whose eventSequence advances after an edit. Publishing is deliberately left to the owner -- the docs still describe the clone-and-tsx install path, which stays correct until the package is live. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Bundles the CLI, MCP server, and daemon into
dist/with esbuild (scripts/build.mjs) and makes the root manifest a publishablecrosscodepackage exposing thecrosscodeandcrosscode-mcpbins, replacing a state wheretsc --noEmitemitted nothing, every package wasprivate, and bothbinentries pointed at TypeScript sources Node cannot execute. It also fixes two failures that only appear after install: the MCP bootstrap hardcoded the monorepotsx+apps/daemon/src/main.tslayout and swallowed the failure viastdio: "ignore", so an installed package reportedDAEMON_UNAVAILABLEforever with no diagnostic, and the CLI's main-module guard comparedimport.meta.urlagainstargv[1]as given, so the symlinked installed binary parsed nothing and exited silently.resolveDaemonLaunchnow prefers the bundled daemon run byprocess.execPath, keeps an existence-checked monorepo fallback sopnpm mcpstill works, and surfaces a message naming what it tried. Touches the CLI, MCP server, build tooling, and docs.Why
Nothing else in this repo turns it into something a stranger can install and run, and bundling sidesteps the cross-package relative imports (
../../daemon/src/client.js) that escape any per-package tarball root. The build fails if anything fromnode_modulesgets inlined, so a bundled-but-undeclared dependency can no longer pass in the monorepo and break only once the tarball is installed elsewhere.Testing
Verified from a packed tarball installed outside this repo on Node 24 alone:
--help,init --json,status --json, andcommands --jsonall work; an MCPinitialize+tools/listhandshake returns 22 tools; the bootstrap starts a real daemon (node .../dist/daemon.js) whoseeventSequenceadvances 0 to 2 after an edit; and both spawn-failure modes produce a message naming the paths tried. Four newbootstrap.test.tscases cover the launch resolution,pnpm audit --audit-level highis clean, andpnpm install --frozen-lockfileis in sync. One intermediate full-suite run hit the pre-existing wall-clock flake inws-client.test.ts > reconnects with increasing backoff; it passed standalone, on a stashed clean tree, and in the final run.pnpm buildpassespnpm testpasses - 282 passed, 19 skipped (278 baseline + 4 new)pnpm test:postgrespasses - 16 passedSecurity / trust-boundary impact
None. No change to auth, RLS, checkpoint/materialization safety, or excluded paths. The daemon is now spawned from a path derived from the installed module and checked for existence, rather than a
../../..guess, which narrows rather than widens what gets executed.Related issues
Implements plan 01 (distribution / npm package).
npm publishis deliberately not run - that is the owner's call, anddocs/install-prompt.md,docs/mcp-clients.md, and the site's install snippet still describe the clone-and-tsxpath, which stays correct until the package is live and should be switched over together.🤖 Generated with Claude Code